﻿using HarmonyLib;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Events;

namespace StationPriority {
    class StationPatch {
        #region "变量"
        // 供货优先级调整组件
        private static RectTransform supplyTransform;
        private static UnityEngine.UI.Slider supplyPrioritySlider;
        private static UnityEngine.UI.Text supplyPriorityValue;
        private static UnityEngine.UI.Text supplyPriorityText;

        private static readonly Dictionary<int, StationPatchContext> contextMap
            = new Dictionary<int, StationPatchContext>();
        private static StationPatchContext currentContext;

        private static readonly Dictionary<int, StationSupplyDemandPair> localPairs
            = new Dictionary<int, StationSupplyDemandPair>();

        private static UIStationWindow uiStationWindow;

        // 存档文件后缀
        private const string SaveExt = ".stationpriority";
        #endregion

        #region "用户界面处理"
        [HarmonyPostfix]
        [HarmonyPatch(typeof(UIStationWindow), "_OnCreate")]
        static void UIStationWindow__OnCreate(UIStationWindow __instance) {
            //Debug.Log("StationPriority: UIStationWindow__OnCreate");

            // 创建供货优先级界面
            supplyTransform = Object.Instantiate(
                __instance.minDeliverDroneGroup,
                __instance.minDeliverDroneGroup.position,
                __instance.minDeliverDroneGroup.rotation,
                __instance.minDeliverDroneGroup.parent);

            UnityEngine.UI.Text[] labels = supplyTransform.gameObject.GetComponentsInChildren<UnityEngine.UI.Text>();
            supplyPriorityText = labels[0];
            supplyPriorityValue = labels[1];

            supplyPrioritySlider = supplyTransform.gameObject.GetComponentInChildren<UnityEngine.UI.Slider>();
            supplyPrioritySlider.minValue = 0;
            supplyPrioritySlider.maxValue = 1000;
            supplyPrioritySlider.value = Configs.DefaultPriority;

            supplyTransform.gameObject.SetActive(false);
        }

        [HarmonyPostfix]
        [HarmonyPatch(typeof(UIStationWindow), "_OnRegEvent")]
        static void UIStationWindow__OnRegEvent() {
            //Debug.Log("StationPriority: UIStationWindow__OnRegEvent");
            supplyPrioritySlider.onValueChanged.AddListener(new UnityAction<float>(OnSupplyPrioritySliderValueChange));
        }

        [HarmonyPostfix]
        [HarmonyPatch(typeof(UIStationWindow), "_OnUnregEvent")]
        static void UIStationWindow__OnUnregEvent() {
            //Debug.Log("StationPriority: UIStationWindow__OnUnregEvent");
            supplyPrioritySlider.onValueChanged.RemoveAllListeners();
        }

        [HarmonyPostfix]
        [HarmonyPatch(typeof(UIStationWindow), "OnStationIdChange")]
        static void UIStationWindow_OnStationIdChange(UIStationWindow __instance) {
            //Debug.Log("StationPriority: UIStationWindow_OnStationIdChange");

            Traverse traverse = Traverse.Create(__instance);
            traverse.Field("event_lock").SetValue(true);
            if (__instance.active) {
                if (__instance.stationId == 0 || __instance.factory == null) {
                    __instance._Close();
                    return;
                }
                StationComponent stationComponent = __instance.transport.stationPool[__instance.stationId];
                if (stationComponent == null || stationComponent.id != __instance.stationId) {
                    __instance._Close();
                    return;
                }

                uiStationWindow = __instance;

                currentContext = CreateOrFindContext(__instance.stationId);


                // 修改界面大小 & 修改优先级调整组件位置
                float heightAdd = 0f;
                Vector2 anchoredPosition;
                if (stationComponent.isCollector) {
                    supplyTransform.gameObject.SetActive(false);
                } else if (stationComponent.isVeinCollector) {
                    supplyTransform.gameObject.SetActive(false);
                } else if (stationComponent.isStellar) {
                    // 星级物流运输站
                    heightAdd = Configs.PropLineHeight;
                    supplyTransform.gameObject.SetActive(true);

                    if (!__instance.minPilerGroup.gameObject.activeSelf) {
                        anchoredPosition = __instance.minDeliverVesselGroup.anchoredPosition;
                    } else {
                        anchoredPosition = __instance.minPilerGroup.anchoredPosition;
                    }
                    anchoredPosition.y -= Configs.PropLineHeight;
                    supplyTransform.anchoredPosition = anchoredPosition;
                } else {
                    // 物流运输站
                    heightAdd = Configs.PropLineHeight;
                    supplyTransform.gameObject.SetActive(true);
                    if (!__instance.minPilerGroup.gameObject.activeSelf) {
                        anchoredPosition = __instance.minDeliverDroneGroup.anchoredPosition;
                    } else {
                        anchoredPosition = __instance.minPilerGroup.anchoredPosition;
                    }
                    anchoredPosition.y -= Configs.PropLineHeight;
                    supplyTransform.anchoredPosition = anchoredPosition;
                }
                Vector2 sizeDelta = __instance.windowTrans.sizeDelta;
                sizeDelta.y += heightAdd;
                __instance.windowTrans.sizeDelta = sizeDelta;

                // 修改下面板位置
                anchoredPosition = __instance.panelDownTrans.anchoredPosition;
                anchoredPosition.y += heightAdd;
                __instance.panelDownTrans.anchoredPosition = anchoredPosition;

                // 修改界面组件的值
                supplyPrioritySlider.value = currentContext.SupplyPriority;
                supplyPriorityText.text = "供货优先级".Translate();
                supplyPriorityValue.text = $"{currentContext.SupplyPriority}";
            }
            traverse.Field("event_lock").SetValue(false);
        }

        private static void OnSupplyPrioritySliderValueChange(float value) {
            if (currentContext == null) {
                return;
            }

            int newValue = Mathf.CeilToInt(value);
            if (newValue != currentContext.SupplyPriority) {
                currentContext.SupplyPriority = Mathf.CeilToInt(value);
                supplyPriorityValue.text = $"{currentContext.SupplyPriority}";
                RefreshLocalPairs();
            }
            //Debug.Log($"StationPriority:  设置<{currentContext.StationId}>的供货优先级为<{currentContext.SupplyPriority}>");
        }

        private static void RefreshLocalPairs() {
            try {
                if (uiStationWindow == null) {
                    return;
                }

                uiStationWindow.transport.RefreshStationTraffic(uiStationWindow.stationId);
                //Debug.Log("StationPriority: 刷新<LocalPairs>");
            } catch(System.Exception e) {
                Debug.LogError($"StationPriority: RefreshLocalPairs<{e.Message}>");
            }
        }
        #endregion

        #region "存档处理"
        [HarmonyPrefix]
        [HarmonyPatch(typeof(UISaveGameWindow), "DoSaveGame")]
        static void UISaveGameWindow_DoSaveGame(UISaveGameWindow __instance) {
            //Debug.Log("StationPriority: UISaveGameWindow_DoSaveGame");
            DoSaveData(__instance.nameInput.text);
        }

        [HarmonyPrefix]
        [HarmonyPatch(typeof(UIMainMenu), "OnContinueButtonClick")]
        static void UISaveGameWindow_OnContinueButtonClick(UIMainMenu __instance) {
            //Debug.Log("StationPriority: UIMainMenu_OnContinueButtonClick");

            if (Traverse.Create(__instance).Field("locked").GetValue<bool>())
                return;
            if (GameSave.SaveExist(GameSave.LastExit)) {
                GameDesc gameDesc = GameSave.LoadGameDesc(GameSave.LastExit);
                if (gameDesc == null)
                    return;

                DoLoadData(GameSave.LastExit);
            }
        }

        [HarmonyPrefix]
        [HarmonyPatch(typeof(UIAutoSave), "_OnOpen")]
        static void UIAutoSave__OnOpen() {
            //Debug.Log("StationPriority: UIAutoSave__OnOpen");

            if (DoSaveData(GameSave.AutoSaveTmp)) {
                string str1 = GameConfig.gameSaveFolder + GameSave.AutoSaveTmp + SaveExt;
                string str2 = GameConfig.gameSaveFolder + GameSave.AutoSave0 + SaveExt;
                string str3 = GameConfig.gameSaveFolder + GameSave.AutoSave1 + SaveExt;
                string str4 = GameConfig.gameSaveFolder + GameSave.AutoSave2 + SaveExt;
                string str5 = GameConfig.gameSaveFolder + GameSave.AutoSave3 + SaveExt;
                if (File.Exists(str1)) {
                    if (File.Exists(str5))
                        File.Delete(str5);
                    if (File.Exists(str4))
                        File.Move(str4, str5);
                    if (File.Exists(str3))
                        File.Move(str3, str4);
                    if (File.Exists(str2))
                        File.Move(str2, str3);
                    File.Move(str1, str2);
                }
            }
        }

        [HarmonyPrefix]
        [HarmonyPatch(typeof(UILoadGameWindow), "DoLoadSelectedGame")]
        static void UILoadGameWindow_Import(UILoadGameWindow __instance) {
            //Debug.Log("StationPriority: UILoadGameWindow_Import");
            DoLoadData(__instance.selected.saveName);
        }

        [HarmonyPrefix]
        [HarmonyPatch(typeof(GameMain), "End")]
        static void GameMain_End() {
            //Debug.Log("StationPriority: GameMain_End");

            if (!(GameMain.instance != null))
                return;
            if (GameMain.gameTime > 0.0 && GameMain.data.guideComplete) {
                if (GameMain.errored)
                    Debug.LogWarning((object)"StationPriority: Auto save disabled due to a previous error.");
                else if (GameMain.isRunning && !GameSave.dontSaveToLastExit && !DSPGame.IsMenuDemo) {
                    Debug.Log((object)"StationPriority: Auto saving to <last exit>");
                    DoSaveData(GameSave.LastExit);
                }
            }
        }

        private static bool DoSaveData(string saveName) {
            try {
                Debug.Log($"StationPriority: 保存模组数据<{saveName}>");
                string path = GameConfig.gameSaveFolder + saveName + ".stationpriority";

                using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) {
                    using (TextWriter w = new StreamWriter(fileStream)) {
                        foreach (StationPatchContext context in contextMap.Values) {
                            if (context.SupplyPriority != Configs.DefaultPriority) {
                                context.Export(w);
                            }
                        }
                    }
                }

                return true;
            } catch (System.Exception e) {
                Debug.LogError($"StationPriority: 保存模组数据失败<{e.Message}>");
            }
            return false;
        }

        private static void DoLoadData(string saveName) {
            try {
                Debug.Log($"StationPriority: 加载模组数据<{saveName}>");

                contextMap.Clear();
                string path = GameConfig.gameSaveFolder + saveName + SaveExt;

                if (File.Exists(path)) {
                    using (FileStream fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read)) {
                        using (TextReader r = new StreamReader(fileStream)) {
                            for (StationPatchContext context = StationPatchContext.Import(r);
                                context != null;
                                context = StationPatchContext.Import(r)) {
                                contextMap[context.StationId] = context;
                            }
                        }
                    }
                }

                Debug.Log($"StationPriority: 加载了<{contextMap.Count}>个优先级数据");
            } catch (System.Exception e) {
                Debug.LogError($"StationPriority: 读取模组数据失败<{e.Message}>");
            }
        }
        #endregion

        #region "本地物流运输站处理"

        [HarmonyPrefix]
        [HarmonyPatch(typeof(StationComponent), "AddLocalPair")]
        static void StationComponent_AddLocalPair(StationComponent __instance, object[] __args, out bool __runOriginal) {
            int saveLocalPairCount = __instance.localPairCount;

            try {
                int supplyId = (int)__args[0];
                int supplyIndex = (int)__args[1];
                StationPatchContext supplyContext = CreateOrFindContext(supplyId);
                int supplyPriority = supplyContext.SupplyPriority;

                int demandId = (int)__args[2];
                int demandIndex = (int)__args[3];

                if (__instance.localPairs == null) {
                    __instance.SetLocalPairCapacity(8);
                    SetLocalPairCapacity(__instance.id, 8);
                }
                if (__instance.localPairCount == __instance.localPairs.Length) {
                    __instance.SetLocalPairCapacity(__instance.localPairs.Length * 2);
                    SetLocalPairCapacity(__instance.id, __instance.localPairs.Length * 2);
                }

                StationSupplyDemandPair stationLocalPairs = localPairs[__instance.id];
                stationLocalPairs.Add(supplyId, supplyIndex, supplyPriority,
                    demandId, demandIndex);

                __instance.localPairCount = 0;
                foreach (SupplyDemandPairPatch pairPatch in stationLocalPairs.GetSortedPairs()) {
                    __instance.localPairs[__instance.localPairCount].supplyId = pairPatch.origin.supplyId;
                    __instance.localPairs[__instance.localPairCount].supplyIndex = pairPatch.origin.supplyIndex;
                    __instance.localPairs[__instance.localPairCount].demandId = pairPatch.origin.demandId;
                    __instance.localPairs[__instance.localPairCount].demandIndex = pairPatch.origin.demandIndex;
                    ++__instance.localPairCount;
                }

                __runOriginal = false;
            } catch (System.Exception e) {
                Debug.LogError($"StationPriority: StationComponent_AddLocalPair<{e.Message}>");

                __instance.localPairCount = saveLocalPairCount;
                __runOriginal = true;
            }
        }

        [HarmonyPrefix]
        [HarmonyPatch(typeof(StationComponent), "ClearLocalPairs")]
        static void StationComponent_ClearLocalPairs(StationComponent __instance) {
            lock (localPairs) {
                if (localPairs.ContainsKey(__instance.id)) {
                    StationSupplyDemandPair stationLocalPairs = localPairs[__instance.id];
                    stationLocalPairs.Clear();
                }
            }
        }

        [HarmonyPrefix]
        [HarmonyPatch(typeof(StationComponent), "InternalTickLocal")]
        static void StationComponent_InternalTickLocal(StationComponent __instance) {
            __instance.localPairProcess = 0;
        }

        private static void SetLocalPairCapacity(int stationId, int capacity) {
            lock (localPairs) {
                if (!localPairs.ContainsKey(stationId)) {
                    localPairs[stationId] = new StationSupplyDemandPair(stationId, capacity);
                } else {
                    StationSupplyDemandPair stationLocalPairs = localPairs[stationId];
                    if (capacity <= stationLocalPairs.Pairs.Length)
                        return;
                    stationLocalPairs.SetCapacity(capacity);
                }
            }
        }

        #endregion

        #region "其他"
        private static StationPatchContext CreateOrFindContext(int stationId) {
            lock (contextMap) {
                if (contextMap.ContainsKey(stationId)) {
                    return contextMap[stationId];
                } else {

                    //Debug.Log($"StationPriority: 为<{stationId}>创建<PatchContext>");
                    StationPatchContext context = new StationPatchContext(
                        stationId, Configs.DefaultPriority);
                    contextMap[stationId] = context;
                    return context;
                }
            }
        }
        #endregion
    }
}
